package com.yisin.commun.modal;

import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

import javax.websocket.EndpointConfig;
import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;

public class Client {
	private Session session;
	private Socket socket;
	private ClientType type;
	private EndpointConfig config;
	private Basic remote;
	private String uuid;
	private String sid;

	private String groupId;
	private String superGroupId;

	private String queryString;

	private String attr1;
	private int attr2;
	private Object attr;
	private Map<String, Object> attrMap = new HashMap<String, Object>();
	private int status;

	public Client() {
	}

	public Client(Session session) {
		this.type = ClientType.WEBSOCKET;
		this.session = session;
	}

	public Client(Socket socket) {
		this.type = ClientType.SOCKET;
		this.socket = socket;
	}

	public enum ClientType {
		WEBSOCKET, SOCKET
	}

	public Session getSession() {
		if (isWebSocket()) {
			return session;
		}
		return null;
	}

	public Client setSession(Session session) {
		this.session = session;
		return this;
	}

	public EndpointConfig getConfig() {
		if (isWebSocket()) {
			return config;
		}
		return null;
	}

	public Client setConfig(EndpointConfig config) {
		this.config = config;
		return this;
	}

	public String getUuid() {
		return uuid;
	}

	public Client setUuid(String uuid) {
		this.uuid = uuid;
		return this;
	}

	public String getQueryString() {
		return queryString;
	}

	public Client setQueryString(String queryString) {
		this.queryString = queryString;
		return this;
	}

	public boolean isWebSocket() {
		return type != null && type == ClientType.WEBSOCKET;
	}

	public boolean isSocket() {
		return type != null && type == ClientType.SOCKET;
	}

	/**
	 * WebSocket关闭时的状态：<br>
	 * <b>1000</b></b><br>
	 * 表示正常关闭，意思是建议的连接已经完成了。<br>
	 * <b>1001</b><br>
	 * 表示端点“离开”（going away），例如服务器关闭或浏览器导航到其他页面。<br>
	 * <b>1002</b><br>
	 * 表示端点因为协议错误而终止连接。<br>
	 * <b>1003</b><br>
	 * 表示端点由于它收到了不能接收的数据类型（例如，端点仅理解文本数据，但接收到了二进制消息）而终止连接。<br>
	 * <b>1004</b><br>
	 * 保留。可能在将来定义其具体的含义。<br>
	 * <b>1005</b><br>
	 * 是一个保留值，且不能由端点在关闭控制帧中设置此状态码。它被指定用在期待一个用于表示没有状态码是实际存在的状态码的应用中。<br>
	 * <b>1006</b><br>
	 * 是一个保留值，且不能由端点在关闭控制帧中设置此状态码。它被指定用在期待一个用于表示连接异常关闭的状态码的应用中。<br>
	 * <b>1007</b><br>
	 * 表示端点因为消息中接收到的数据是不符合消息类型而终止连接（比如，文本消息中存在非UTF-8[RFC3629]数据）。<br>
	 * <b>1008</b><br>
	 * 表示端点因为接收到的消息违反其策略而终止连接。这是一个当没有其他合适状态码（例如1003或1009）
	 * 或如果需要隐藏策略的具体细节时能被返回的通用状态码。<br>
	 * <b>1009</b><br>
	 * 表示端点因接收到的消息对它的处理来说太大而终止连接。<br>
	 * <b>1010</b><br>
	 * 表示端点（客户端）因为它期望服务器协商一个或多个扩展，但服务器没有在WebSocket握手响应消息中返回它们而终止连接。
	 * 所需要的扩展列表应该出现在关闭帧的/reason/部分。<br>
	 * 注意，这个状态码不能被服务器端使用，因为它可以失败WebSocket握手。<br>
	 * <b>1011</b><br>
	 * 表示服务器端因为遇到了一个不期望的情况使它无法满足请求而终止连接。<br>
	 * <b>1015</b><br>
	 * 是一个保留值，且不能由端点在关闭帧中被设置为状态码。它被指定用在期待一个用于表示连接由于执行TLS握手失败而关闭的状态码的应用中（比如，
	 * 服务器证书不能验证）。<br>
	 * 
	 * @return
	 */
	public int getStatus() {
		return status;
	}

	public Client setStatus(int status) {
		this.status = status;
		return this;
	}

	public String getGroupId() {
		return groupId;
	}

	public Client setGroupId(String groupId) {
		this.groupId = groupId;
		return this;
	}

	public String getSuperGroupId() {
		return superGroupId;
	}

	public Client setSuperGroupId(String superGroupId) {
		this.superGroupId = superGroupId;
		return this;
	}

	public Client setSid(String sid) {
		this.sid = sid;
		return this;
	}
	
	public String getSid() {
		return sid;
	}
	
	public Client set(String key, Object value){
		this.attrMap.put(key, value);
		return this;
	}
	
	public Object get(String key){
		return attrMap.get(key);
	}

	public String getAttr1() {
		return attr1;
	}

	public Client setAttr1(String attr1) {
		this.attr1 = attr1;
		return this;
	}

	public int getAttr2() {
		return attr2;
	}

	public Client setAttr2(int attr2) {
		this.attr2 = attr2;
		return this;
	}

	public Object getAttr() {
		return attr;
	}

	public Client setAttr(Object attr) {
		this.attr = attr;
		return this;
	}

	private Basic getRemote() {
		if (remote == null) {
			remote = session.getBasicRemote();
		}
		return remote;
	}

	public boolean send(DataPackage dataPackage) throws Exception {
		boolean result = false;
		if (isWebSocket()) {
			getRemote().sendText(dataPackage.toString());
			result = true;
		} else if (isSocket()) {
			OutputStream os = null;
			try {
				if(socket.isClosed()){
					throw new SocketException("Socket is closed" + sid);
				} else {
					os = socket.getOutputStream();
					os.write(dataPackage.getByte());
					os.flush();
					result = true;
				}
			} catch (Exception e) {
				result = false;
				String ip = socket.getRemoteSocketAddress().toString();
				DataCache.removeBySid(ip);
				throw new SocketException("发送数据到客户端出错：" + sid);
			}
			result = true;
		}
		return result;
	}

	public Client close() {
		try {
			if (isWebSocket()) {
				session.close();
			} else if (isSocket()) {
				socket.close();
			}
		} catch (Exception e) {
		}
		return this;
	}

	public Client clone() {
		Client o = null;
		try {
			o = (Client) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return o;
	}

	public boolean isSelf(Client c) {
		return null != c && c.getSid().equals(sid) && c.getUuid().equals(uuid);
	}

	private class _Client {

	}

}
